package com.gjt.serverSocket;

import com.gjt.event.CompleteConnectionEvent;

import javax.net.SocketFactory;
import javax.net.ssl.*;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.net.InetAddress;

import java.net.Socket;

/**
 * 连接安全的http 服务器
 *
 * @author Guojt
 * @date 2024/9/18 7:54
 */
public class HTTPSClient {
    private static int isConnected = 0; // 0 未连接，1 已连接


    public static void main(String[] args) {
        if (args.length == 0) {
            System.out.println("请输入参数");
            return;
        }
        int port = 443; //https 的默认端口
        String host = args[0];
        SSLSocketFactory factory = (SSLSocketFactory) SSLSocketFactory.getDefault();


        SSLSocket socket = null;
        try {
            socket = (SSLSocket) factory.createSocket(host, port);

            // 启动所有密码组
            String[] supportedCipherSuites = factory.getSupportedCipherSuites();
            socket.setEnabledCipherSuites(supportedCipherSuites);


            // 处理握手结束事件 可以让客户端 异步的去链接 连接成功 通知客户做一些事情(只是添加到socket 中并没有执行握手 )
//            socket.addHandshakeCompletedListener(new HandshakeCompletedListener() {
//                @Override
//                public void handshakeCompleted(HandshakeCompletedEvent event) {
//                    System.out.println("握手结束");
//
//                    /**
//                     * 即密码组  TLS_AES_256_GCM_SHA384
//                     *  每个算法分4部分：协议，密钥，加密算法，校验和
//                     *  TLS：传输层安全协议
//                     *  AES_256_GCM：高级加密算法
//                     *  GCM：加密模式，结合了加密算法和认证机制
//                     *  SHA384：散列算法，用来计算摘要
//                     */
//                    System.out.println("加密套件为：" + event.getCipherSuite());
//
//                    //Session(1726829560947|TLS_AES_256_GCM_SHA384)
//                    System.out.println("会话为：" + event.getSession());
//
//                    // www.usps.com
//                    System.out.println("通信对方是：" + event.getSession().getPeerHost());
//
//                    isConnected = 1;
//                }
//            });
            CompleteConnectionEvent completeConnectionEvent = new CompleteConnectionEvent(isConnected);
            socket.addHandshakeCompletedListener(completeConnectionEvent);

//            socket.getSession().invalidate();

            // 该方法显式的执行一次SSL握手; 重新认证一个连接（丢弃之前的证书和密钥）
//            socket.startHandshake();

            Thread.sleep(3000);


            /**
             *  客户端安全socket
             */
            // 确定Socket 是否需要在第一次握手时使用认证。 true: 不会提供自行认证； false: 会尝试自行认证 ; 这个属性任何socket 只能设置一次
            socket.setUseClientMode(true);

            // 告诉SOCKET 是否在第一次握手时使用认证。
            socket.getUseClientMode();


            /**
             * 服务端安全socket
             */
            // 参数 false : 要求与他相连的所以客户都需要自行认证 或者不认证； 如果socket 不在服务端抛出非法参数异常
            socket.setNeedClientAuth(false);

            // 当socket 需要认证时 true
            socket.getNeedClientAuth();

            // 为了避免多个Socket 共享会话
            socket.setEnableSessionCreation(false);

            // 允许多个Socket 会话 返回true
            System.out.println(socket.getEnableSessionCreation());

            // 连接成功
            if (CompleteConnectionEvent.getIsConnected() == 1) {

                socket.removeHandshakeCompletedListener(completeConnectionEvent);
                //
                OutputStreamWriter out = new OutputStreamWriter(socket.getOutputStream(), "UTF-8");
                // https 在get 行中需要完全url
                out.write("GET http://" + host + "/ HTTP/1.1\r\n");
                out.write("Host: " + host + "\r\n");
                out.write("\r\n");
                out.flush();

                // 读取响应
                BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()));

                // 读取首部
                String s;
                while (!(s = in.readLine()).equals("")) {
                    System.out.println(s);
                }

                System.out.println();

//            读取长度
                String contentLength = in.readLine();
                System.out.println(contentLength + "ssssss");
                int length = Integer.MAX_VALUE;


                try {
                    length = Integer.parseInt(contentLength.trim(), 16);
                } catch (NumberFormatException e) {
                    System.out.println("这个服务器在响应体的第一行 没有发送content-length");
                    throw new RuntimeException(e);
                }
                System.out.println(contentLength);

                int c = 0;
                int i = 0;
                while ((c = in.read()) != -1 && i++ < length) {
                    System.out.write(c);
                }
                System.out.println();
            } else {
                System.out.println("还没有连接成功");
            }

        } catch (IOException e) {
            throw new RuntimeException(e);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        } finally {
            try {
                if (socket != null) socket.close();
            } catch (IOException e) {
            }
        }

    }


    /**
     * 创建安全客户端Socket
     * <p>
     * 从javax.net.ssl.SSLSocketFactory使用createSocket方法创建一个安全套接字，
     */

    private void create() {

        SocketFactory aDefault = SSLSocketFactory.getDefault();

        try {
            String host = "152.136.138.218";

            // 没有连接的安全的socket
            Socket socket = aDefault.createSocket();

            // 连接到指定主机和端口的Socket, 无法连接，则抛出io异常
            Socket socket1 = aDefault.createSocket("152.136.138.218", 80);
            Socket socket2 = aDefault.createSocket(InetAddress.getByAddress(new byte[]{127, 0, 0, 1}), 80);

            // 创建并返回一个从指定本地网络接口和端口连接到指定主机和端口的Socket
            Socket socket3 = aDefault.createSocket("152.136.138.218", 80, InetAddress.getByAddress(new byte[]{127, 0, 0, 1}), 80);
            Socket socket4 = aDefault.createSocket(InetAddress.getByAddress(host.getBytes()), 80, InetAddress.getByAddress(new byte[]{127, 0, 0, 1}), 80);


            // 这个以一个连接到代理服务器的现有Socket为起点,返回有代理服务器指定的主机和 端口的Socket。 最后的参数，当这个Socket 被关闭时，代理服务器的连接也会关闭。
            Socket socket5 = new Socket();
//            Socket socket6 = aDefault.createSocket(socket5,"152.1636.138.218",80, true);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }

    }
}
